package edu.um.expression;

import edu.um.analyzer.exception.TypeMismatchException;
import edu.um.io.Console;
import edu.um.token.TokenKind;
import edu.um.variable.ArrayVariable;
import edu.um.variable.Variable;
import edu.um.variable.VariableType;

public enum ValidationStrategy {
  ASIGNATION {

    @Override
    public void validate(ExpressionTreeNode firstExpression,
        ExpressionTreeNode secondExpression) throws TypeMismatchException {
      validateTypes(firstExpression, secondExpression);
      validateValues(firstExpression, secondExpression);
    }

    private void validateTypes(ExpressionTreeNode firstExpression,
        ExpressionTreeNode secondExpression) throws TypeMismatchException {
      Variable secondVariable = secondExpression.getVariable();
      if (secondVariable != null) {
        VariableType secondType = secondVariable.getType();
        Variable variable = firstExpression.getVariable();
        VariableType type = variable.getType();
        if (type != secondType
            && !(type == VariableType.WORD && secondType == VariableType.BYTE)) {
          throw new TypeMismatchException(type, secondType, secondExpression.getToken());
        }
      }
    }

    private void validateValues(ExpressionTreeNode firstExpression,
        ExpressionTreeNode secondExpression) throws TypeMismatchException {
      Variable variable = firstExpression.getVariable();
      VariableType type = variable.getType();
      Object value = secondExpression.evaluate();
      if (!type.admitsValue(value)) {
        throw new TypeMismatchException(type, value, secondExpression.getToken());
      }
      variable.setValue(value);
      Console.debug("Variable set value: " + variable.getName() + " = "
          + variable.getValue());
    }

  },
  COMPARATION {

    @Override
    public void validate(ExpressionTreeNode firstExpression,
        ExpressionTreeNode secondExpression) throws TypeMismatchException {
      TokenKind firstKind = getTokenKind(firstExpression);
      TokenKind secondKind = getTokenKind(secondExpression);
      if ((firstKind == TokenKind.STRING_BLANCO || firstKind == TokenKind.STRING_SUBRAYADO)
          && !(secondKind == TokenKind.STRING_BLANCO || secondKind == TokenKind.STRING_SUBRAYADO)) {
        throw new TypeMismatchException("Expression are not comparable",
            secondExpression.getToken());
      }
      if (!(firstKind == TokenKind.STRING_BLANCO || firstKind == TokenKind.STRING_SUBRAYADO)
          && (secondKind == TokenKind.STRING_BLANCO || secondKind == TokenKind.STRING_SUBRAYADO)) {
        throw new TypeMismatchException("Expression are not comparable",
            secondExpression.getToken());
      }
    }

    private TokenKind getTokenKind (ExpressionTreeNode expression) {
      TokenKind kind = expression.getToken().getKind();
      if (kind == TokenKind.ID) {
        Variable variable = expression.getVariable();
        kind = getTokenKind(variable.getType(), variable);
      }
      return kind;
    }

    private TokenKind getTokenKind(VariableType variableType, Variable variable) {
      switch (variableType) {
        case WORD: return TokenKind.CONST_WORD;
        case BYTE: return TokenKind.CONST_BYTE;
        case STRING_BLANCO: return TokenKind.STRING_BLANCO;
        case STRING_SUBRAYADO : return TokenKind.STRING_SUBRAYADO;
        case ARRAY: return getTokenKind(((ArrayVariable) variable).getSubType(), null);
      }
      return null;
    }
  };

  public abstract void validate(ExpressionTreeNode firstExpression,
      ExpressionTreeNode secondExpression) throws TypeMismatchException;

}
